/**
 * Copyright 2007-2008 Vidar Svansson
 *     
 * This file is part of JAdapter.
 * 
 * JAdapter is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * JAdapter is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with JAdapter.  If not, see <http://www.gnu.org/licenses/>.
 */


package org.jadapter.registry;

import org.jadapter.AdapterFactory;
import org.jadapter.JAdapter;
import org.jadapter.NamedComponent;
import org.jadapter.Query;
import org.jadapter.beans.UtilityBean;
import org.jadapter.func.Nil;

/** 
 * Convenient super class for classes that implement all the component interfaces.
 * <br><br />
 * Version 0.1 is focused on {@link AdapterRegistry}.
 * 
 * @author Vidar Svansson
 * 
 * @since 0.1
 */
public abstract class AbstractComponentRegistry 
        extends AbstractAdapterRegistry implements ComponentRegistry {
   

    /* used as the context for null adapters */
    private static Nil nil = new Nil();

    
    final public <T> Query<T> query(T ... t){
        return new Query<T>(this, t);
    }
    
    final public <T> Query<T> query(Iterable<T> iterable){
        return new Query<T>(this, iterable);
    }    
    
    final public Query<String> query(String string){
        return new Query<String>(this, string);
    }
    
    /** Create an instance of the given type */
    @SuppressWarnings("unchecked") 
    final public <T> T create(Class<T> type) {
        return adapt(type, nil);
    }    
     
    /** Get an instance of the given some context object */
    @SuppressWarnings("unchecked")
    final public  <T> T adapt(Class<T> type, Object context) {
        return adapt(type, context, JAdapter.ANY);
    }    
    
    
    @SuppressWarnings("unchecked")
    final protected <T> T self(Class<T> type, Object context) {
        if(type.isInstance(context)){
            return (T)context;
        }
        else return null;
    }    

    
    @SuppressWarnings("unchecked")
    final public <I,C> void registerAdapter(
            Class<I> provides, 
            Class<?> adapter, 
            Class<C> adaptable) {
        JAdapter<I,C> jadapter = new JAdapter<I,C>();
        jadapter.setProvides(provides);
        jadapter.setFor(adaptable);
        jadapter.setAdapter(adapter);
        register(jadapter);
    }     
    
    

    /** Register a factory class for a type 
     * 
     * @param <I> Generic type of the interface provided by the factory.
     * @param type the provided type
     * @param implementation implementing the type
     */
    final public <I> void register(Class<I> type, 
            Class<? extends I> implementation) {
        
        register(type, implementation, Nil.class);
    }    
    
    final public <I,C> void register(Class<I> type, 
            I utility, Class<C> context) {
        
        UtilityBean<I,C> util = new UtilityBean<I,C>();
        util.setFor(context);
        util.setProvides(type);
        util.setUtility(utility);
               
        register(util);
    }
    
    
    final public boolean provides(AdapterFactory<?,?> factory, Class<?> iface, Object context) {
        return provides(factory, iface, context, NamedComponent.ANY);
    }
    
    final public boolean provides(AdapterFactory<?,?> factory,Class<?> iface, Object context, String name) {
        Class<?> klass = context instanceof Class ? (Class<?>) context : context.getClass();  
        boolean assignable = iface.isAssignableFrom(factory.provides()) &&
            factory.context().isAssignableFrom(klass);
        if(factory instanceof NamedComponent)
            return assignable && name.equals(((NamedComponent)factory).name());
        
        return assignable;
    }   
    
       

}
